home *** CD-ROM | disk | FTP | other *** search
/ PsL Monthly 1993 December / PSL Monthly Shareware CD-ROM (December 1993).iso / prgmming / dos / c / evalx.com / EVALX.C < prev    next >
Encoding:
C/C++ Source or Header  |  1990-09-15  |  11.4 KB  |  486 lines

  1. /*    +-----------------------------------+
  2.  *    |                                   |
  3.  *    |              EVALX.C              |
  4.  *    |                                   |
  5.  *    +-----------------------------------+
  6.  *
  7.  *    Algebraic Expression Evaluator/Parser
  8.  *
  9.  *      James P. Hawkins  - WA2WHV
  10.  *    P.O. Box 9146,
  11.  *     Trenton, NJ 08650
  12.  *
  13.  * calling format:
  14.  *
  15.  *    double value;
  16.  *    double evalx();
  17.  *    value = evalx(expstr)
  18.  *
  19.  *    where:
  20.  *        expstr is the char string representing the expression
  21.  *            to be evaluated. The string must be NULL terminated.
  22.  *        The expression may contain digits representing decimal numbers
  23.  *        or the following math functions:
  24.  *            sin(expstr) - sine of an angle in radians
  25.  *            cos(expstr) - cosine of an angle in radians
  26.  *            exp(expstr) - exponentiate
  27.  *            log(expstr) - natural log
  28.  *            int(expstr) - integerize (truncate to right of dec .)
  29.  *            sqr(expstr) - square root
  30.  *            abs(expstr) - absolute value
  31.  *            atn(expstr) - arctan
  32.  *            rnd(expstr) - random number gen
  33.  *            tan(expstr) - tangent of an angle
  34.  *            fact(expstr) - factorial
  35.  *         expression may contain the operators:
  36.  *            +   addition
  37.  *            -   subtraction
  38.  *            /   division
  39.  *            *   multiplication
  40.  *            ()  parenthesis (can be nested)
  41.  *            ^   exponentiate
  42.  *  Errors:
  43.  *    Test errorflg for non-zero;
  44.  *    See evalx.h for definitions of error constants.
  45.  *
  46.  */
  47. #include    "evalx.h"
  48. #define    STKSIZ    6    /* stack size */
  49.  
  50. #define    skipspace() {while(*expptr == ' ' || *expptr == '\t') *expptr++;}
  51.  
  52. int    errorflg = 0;
  53. char    *eoexpr;    /* Expression scan place holder */
  54.  
  55. double    pow();
  56. double    atof();
  57. double    mathcall();
  58.  
  59.  
  60. /* -------------------------------------------------------------------- */
  61. /*
  62.  * Evaluate Expression - recursive
  63.  * exps is pointer to ASCII expression string.
  64.  */
  65. double evalx(char *exps)
  66. {
  67.     char    *expptr,    /* expression string pointer */
  68.         field[80];    /* expression field */
  69.  
  70.     int    o, n,        /* indicies for opstack & opndstack */
  71.         savptr,        /* save stack for pointer */
  72.         type,        /* field type returned by class */
  73.         opflag;        /* set when operator encountered
  74.                     cleared when variable or non-op
  75.                      encountered. Used to deal with
  76.                       unaries by forcing a 0.0 on the
  77.                     number stack */
  78.  
  79.     char    opstack[STKSIZ];    /* operator stack */
  80.     double    opndstack[STKSIZ];    /* number stack        */
  81.  
  82.  
  83.     expptr = exps;    /* init expression string pointer */
  84.     o = n = STKSIZ;    /* init operator and
  85.                 variable stack pointer */
  86.     opflag = 0;    /* init opflag */
  87.  
  88.     errorflg = 0;
  89.  
  90.     /*
  91.      * Scan expression string until NULL is encountered.
  92.      * Encounter of a paren calls this function recursively.
  93.      * Encounter of recognized functions calls mathcall() which
  94.      * determines which function to call based on the string name
  95.      * of the function.  The function may contain yet another
  96.      * algebraic expression and so on.
  97.      */
  98.     while(*expptr != '\0')
  99.     {
  100.         if((type = class(&expptr, field)) < 0) /* get field and type */
  101.         {
  102.             errorflg = ERRSYNTAX;    /* EXPR SYNTAX */
  103.             errorflg = ERRFATAL;    /* FATAL ERROR */
  104.             return(0.0);/* return zero on error */
  105.         }
  106.  
  107.         switch(type)
  108.         {
  109.         case NMCLASS:    /* NUMERIC FIELD */
  110.             opndstack[--n] = atof(field);
  111.             opflag = 0;
  112.             break;
  113.         case FNCLASS:    /* FUNCTION REFERENCE */
  114.             opndstack[--n] = mathcall(field);
  115.             opflag = 0;
  116.             break;
  117.         case OPCLASS:    /* OPERATOR */
  118.             switch(*field)
  119.             {
  120.             case '(': /* begin of expres */
  121.                 opndstack[--n] = evalx(expptr);
  122.                 opflag = 0;
  123.                 expptr = eoexpr; /* put pointer
  124.                            to correct place */
  125.                 break;
  126.             case ')': /* end of express */
  127.                 /*
  128.                  * eval the rest of the expr
  129.                  * between the parens
  130.                  */
  131.                 cleanup(opndstack,opstack,&n,&o);
  132.                 eoexpr = expptr;
  133.                 return(opndstack[n]);
  134.                 break;
  135.             case '+':
  136.             case '-':
  137.             case '*':
  138.             case '/':
  139.             case '^':
  140.                 /*
  141.                  * Handle unaries by forcing a 0.0 onto
  142.                  * the opndstack before stacking the operator.
  143.                  * This occurs when two operators in a row are
  144.                  * encountered or the variable stack is empty
  145.                  * when an operator encountered.
  146.                  */
  147.                 if(opflag || n == STKSIZ)
  148.                     opndstack[--n] = 0.0;
  149.                 opflag = 1;
  150.                 stackop(*field,opndstack,opstack,&n,&o);
  151.                 break;
  152.             default:
  153.                 break;
  154.             }
  155.             break;
  156.         default:
  157.             errorflg = ERRILLVAR;
  158.             break;
  159.         }
  160.         /*
  161.          * Skip trailing white space so that
  162.          * test at top of this loop fails when
  163.          * it's supposed to.
  164.          */
  165.         skipspace();
  166.     }    /* END OF WHILE LOOP */
  167.  
  168.     cleanup(opndstack,opstack,&n,&o);
  169.     eoexpr = expptr; /* Save place */
  170.     return(opndstack[n]);
  171. }
  172.  
  173. /* -------------------------------------------------------------------- */
  174. /*
  175.  * //// STACK OPERATOR IF PRECIDENCE TEST IS PASSED ////
  176.  */
  177. stackop(op,opndstack,opstack,n,o)
  178. char    op;
  179. double    opndstack[];
  180. char    opstack[];
  181. int    *n;
  182. int    *o;
  183. {
  184.  
  185.     /*
  186.      * If precedence of current operator is
  187.      * HIGHER than the operator on top of the stack
  188.      * or the OPERATOR stack is EMPTY,
  189.      * the precedence test has "passed" so just push
  190.      * the current operator onto the operator stack.
  191.      */
  192.     if(*o == STKSIZ)    /* if stack is empty */
  193.     {
  194.         opstack[--(*o)] = op;    /* stack operator
  195.                         precedence done only
  196.                          if stack NOT empty */
  197.         return;
  198.     }
  199.     if(preced(op) > preced(opstack[*o]))
  200.     {
  201.         opstack[--(*o)] = op;
  202.     }
  203.     else
  204.     {
  205.         /*
  206.          * OTHERWISE if the current operator
  207.          * precedence is LESS than or EQUAL to the
  208.          * operator on the top of the stack
  209.          * "uopndstack" the operators from the operator
  210.          * stack until either the stack is empty
  211.          * or the precedence test passes
  212.          */
  213.         while((preced(op) <= preced(opstack[*o])) && *o < STKSIZ)
  214.         {
  215.             if(unstack(opndstack,opstack,n,o) < 0)
  216.             {
  217.                 return(-1); /* break loop if bad operator
  218.                         encountered */
  219.             }
  220.         }
  221.  
  222.         opstack[--(*o)] = op;
  223.     }
  224. }
  225.  
  226. /* -------------------------------------------------------------------- */
  227. /*
  228.  *
  229.  * //// RETURN PRECEDENCE OF OPERATOR IN c ////
  230.  */
  231. preced(c)
  232. char c;
  233. {
  234. static char olist[] = "+1-1*2/2^3"; /* list of operators & precedence */
  235.     register int p;    /* scanning register */
  236.  
  237.     for(p=0; olist[p] != '\0'; p += 2)
  238.     {
  239.         if(c == olist[p])
  240.         {
  241.             return(olist[p+1] - '0');
  242.         }
  243.     }
  244.     return(-1);    /* if we get here, we're in trouble! */
  245. }
  246.  
  247. /* -------------------------------------------------------------------- */
  248. /*
  249.  * //// CLEANUP THE REST OF THE EXPRESSION ////
  250.  *
  251.  * "pop and do" operations on opstack until
  252.  * stack empty
  253.  */
  254. cleanup(opndstack,opstack,n,o)
  255. double    opndstack[];
  256. char    opstack[];
  257. int    *n;
  258. int    *o;
  259. {
  260.     while(*o < STKSIZ)
  261.     {
  262.         if(unstack(opndstack,opstack,n,o) < 0)
  263.         {
  264.             return(-1);    /* break loop if bad operator
  265.                         encountered */
  266.         }
  267.     }
  268. }
  269.  
  270. /* -------------------------------------------------------------------- */
  271. /*
  272.  * This is where the actual math operations are performed.
  273.  *
  274.  * Pop operater off top of opstack and perform that operation on
  275.  * the top two numbers on opndstack.
  276.  * Opndstack gets popped and result is left on top.
  277.  * Then POP both stacks.
  278.  */
  279. unstack(opndstack,opstack,n,o)
  280. double    opndstack[];
  281. char    opstack[];
  282. int    *n;
  283. int    *o;
  284. {
  285.     switch(opstack[*o])
  286.     {
  287.         case '^': /* Exponentiate */
  288.             opndstack[*n+1] = pow(opndstack[*n+1],opndstack[*n]);
  289.             break;
  290.         case '*': /* Multiply */
  291.             opndstack[*n+1] = opndstack[*n+1] * opndstack[*n];
  292.             break;
  293.         case '/': /* Divide */
  294.             if(opndstack[*n] == 0.0) /* catch overflow */
  295.             {
  296.                 errorflg = ERROVFLOW; /* OVERFLOW ERR */
  297.                 break;
  298.             }
  299.             opndstack[*n+1] = opndstack[*n+1] / opndstack[*n];
  300.             break;
  301.         case '+': /* Add */
  302.             opndstack[*n+1] = opndstack[*n+1] + opndstack[*n];
  303.             break;
  304.         case '-': /* Subtract */
  305.             opndstack[*n+1] = opndstack[*n+1] - opndstack[*n];
  306.             break;
  307.         default:  /* Shouldn't get here, but flagged just ini case. */
  308.             errorflg = ERRSYNTAX;
  309.             return(-1);
  310.             break;
  311.     }
  312.     (*n)++;        /* pop number stack */
  313.     (*o)++;        /* pop operator stack */
  314.     return(0);
  315. }
  316.  
  317. /* -------------------------------------------------------------------- */
  318.  
  319. /*
  320.  * Function declarations.
  321.  */
  322. double    sin();
  323. double    cos();
  324. double    atan();
  325. double    log();
  326. double    tan();
  327. double    fact();
  328. double    exp();
  329.  
  330. /* --------------------------------------------------------------------- */
  331. /*  SOME EXTRA FUNCTIONS                                                 */
  332. /* --------------------------------------------------------------------- */
  333. double _int(num)
  334. double num;
  335. {
  336.     long    trunc;    
  337.     trunc=num;
  338.     num=trunc;
  339.     return(num);
  340. }
  341. /* --------------------------------------------------------------------- */
  342. double sqroot(num)
  343. double num;
  344. {
  345.     if(num < 0.0)    
  346.     {
  347.         errorflg = ERRSQRT;
  348.     }
  349.     return(pow(num, 0.5));    
  350. }
  351.  
  352. /* --------------------------------------------------------------------- */
  353. /*
  354.  * Abosulute value
  355.  */
  356. double absolute(num)
  357. double num;
  358. {
  359.     double    fabs();
  360.     return(fabs(num));
  361. }
  362.  
  363. /* --------------------------------------------------------------------- */
  364. /*
  365.  * Random number generator.
  366.  */
  367. double rndgen(num)
  368. double    num;
  369. {
  370.     double    rndnum;
  371.     rndnum = rand();    
  372.     return((rndnum/32767.0) * num + 1);    
  373. }
  374.  
  375. /* --------------------------------------------------------------------- */
  376. /*
  377.  * Factorial
  378.  */
  379. double fact(n)
  380. double  n;
  381. {
  382.         long    l;
  383.         double  fact();
  384.         l = n;
  385.     if(l>33 || l<0L)
  386.     {
  387.         errorflg = ERRFACT;
  388.         return(0.0);
  389.     }
  390.  
  391.         if(l == 0 || l == 1)
  392.     {
  393.                 return(1.0);
  394.     }
  395.         else
  396.     {
  397.                 return(l*fact(l-1.0));
  398.     }
  399. }
  400. /* --------------------------------------------------------------------- */
  401. /*
  402.  * This is the math func string and routine
  403.  * dispatch table. Each entry contains the text for the MATH FUNCTION
  404.  * in question and the address of the routine which services it.
  405.  */
  406. struct tbl
  407.     {
  408.         char *cmdtxt;
  409.         double (*func)();
  410.     };
  411. struct tbl mathtbl[] = {
  412.     {"sin",        sin},
  413.     {"cos",        cos},
  414.     {"exp",        exp},
  415.     {"log",        log},
  416.     {"int",        _int},
  417.     {"sqr",        sqroot}, 
  418.     {"abs",        absolute},
  419.     {"atn",        atan},
  420.     {"rnd",        rndgen},    
  421.     {"tan",        tan},
  422.     {"fact",    fact},
  423. /* THIS STUFF NOT INCLUDED YET
  424.     {"cot",        cotang},
  425.     {"clg",        log10},
  426.     {"sgn",        sign},
  427. */
  428.     {0,        0}    
  429.     };
  430. /* -------------------------------------------------------------------- */
  431. /*
  432.  * ////// CALL MATH FUNCTION ////////
  433.  *
  434.  * callin format:
  435.  *    value = mathcall(string);
  436.  *
  437.  *    where: value = floating point value returned by call
  438.  *           string = pointer to a null terminated string containong
  439.  *            the func text and expression within () - 
  440.  *            i.e. "sin(EXPRESSION TEXT)"
  441.  */
  442. double
  443. mathcall(s)
  444. char    s[];
  445. {
  446.     char    funnam[10]; /* func name copied from 's'
  447.                 used for string search */
  448.     register char *x; /* pointer to paren enclosed expression */
  449.     double    evalx(),
  450.         value,    /* final value returned */
  451.         exvalue; /* value of expression before being operated
  452.                  upon by the math func */
  453.     register  int i;        /* index reg. for expediency */
  454.  
  455.     x = s;        /* set pointer to func string */
  456.     /*
  457.      * copy the func name part up to the '('
  458.      * to use for a string search in the table of names
  459.      */
  460.     for(i=0; *x != '(' ;)
  461.     {
  462.         funnam[i++] = *x++;
  463.     }
  464.  
  465.     funnam[i] = '\0';    /* null terminate */
  466.  
  467.     /*
  468.      * compare each string in table with funnam
  469.      * when match is found, call using offset code
  470.      * if end-of-table (null) encountered return 0
  471.      */
  472.     for(i=0; mathtbl[i].cmdtxt != 0 ; i++)
  473.     {
  474.         if(!(strcmp(funnam, mathtbl[i].cmdtxt)))
  475.         {
  476.             x++;
  477.             exvalue = evalx(x); 
  478.             /* Call the function!! */
  479.             value = (*mathtbl[i].func)(exvalue);
  480.             return(value);
  481.         }
  482.     }
  483.     errorflg = ERRFUNC;    /* unknown math function */
  484.     return(0.0);    /* Return zero for unknown math functions */
  485. }
  486.